home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
Monster Media 1996 #15
/
Monster Media Number 15 (Monster Media)(July 1996).ISO
/
utils
/
mt100.zip
/
MT.CPP
< prev
next >
Wrap
C/C++ Source or Header
|
1996-06-04
|
18KB
|
496 lines
// Move To drive/directory.
// Copyright 1996 Jason Hood
// Started: 14 April, 1996.
// Finished: 4 June.
// Will change drive as well as directory.
// Allows use of slash ("/") as well as backslash ("\").
// Can use multiple dots (eg. treats "..." as "../..").
// Can select previous directory by using "mt;" (or "mt ;" if not so lazy).
// Can also select the directory before the previous directory by using ";;".
// Partial directory names, where searches always start from the root.
// "mt @drives" will construct a directory structure file for drives. "mt @"
// will update the directory structure file for drives already in the file.
// The path is specified via mtmem. The filename is "mtdirs.dat".
// Path and previous directories are stored in memory, allocated by mtmem.
// Acknowledgements: Tim Jones' WASTED.PAS for finding directories.
// You are free to use this code, or a portion thereof, as long as an
// appropriate acknowledgement is made.
// Questions, suggestions and comments to hoodj@topaz.cqu.edu.au.
#include <dir.h>
#include <string.h>
#include <iostream.h>
#include <iomanip.h>
#include <fstream.h>
#include <ctype.h>
#include <stdlib.h>
#define version "1.00"
char olddir[MAXDIR], newdir[MAXDIR]; // The current and new directories
char mtdirs[MAXPATH], // Directory structure file
prev[2][MAXDIR]; // Previous directories
char **dirs; // Directories for each drive
int dirnum; // Number of directories on each drive
const int MaxDirs = 1500; // Maximum number of directories/drive
int find(const char* path); // Try and find path
int partdir(int num, char* partial[], char drive); // Find a partial directory
void restore(char drv, const char* rev); // Make a proper pathname
int dirfile(char* drives); // Create directory structure for drives
void finddirs(const char* startdir, const char* parent = "");// Find directories
char* finddirec(struct ffblk &dir, const char* name = ""); // Find dir. name
long index(ofstream& os, char drv); // Index and write drv's directories
int sort(const void* a, const void* b); // How the directories are sorted
int subs(const char* path); // Number of directories in path
void help(); // Display help screen
inline ofstream& wlong(ofstream& os, long num) { // Write a long to a file
os.write((char*)&num, sizeof(long)); // as a binary value
return os;
}
inline ifstream& rlong(ifstream& is, long& num) { // Read a long from a file
is.read((char*)&num, sizeof(long)); // as a binary value
return is;
}
void main(int argc, char* argv[]) {
int bprev = 1, // Update both previous directories
result; // Result from functions
char drive, // Drive specified
*env; // Environment string
unsigned mtmem = 0; // Segment assigned by mtmem.com
if (!(env = getenv("MTMEM")) || *env == '#') {
cout << "You must run \"MTMEM\" first." << endl;
return;
}
// Convert the string into a segment address, assuming it to be valid
for (; *env; env++) mtmem = (mtmem << 4) | (*env & 0x0f);
// It is easier to do this than to muck about with far pointers
movedata(mtmem, 0, _DS, (unsigned)mtdirs, MAXPATH+MAXDIR*2);
olddir[0] = getdisk() + 'A'; // Retrieve the current directory
olddir[1] = ':';
olddir[2] = '\\';
getcurdir(0, olddir+3);
if (argc == 1) { // No parameters
cout << endl
<< " Current directory = " << olddir << endl
<< " Previous directory = " << prev[0] << endl
<< "Before previous directory = " << prev[1] << endl;
return;
}
char *&dir = argv[1], // Alias the first argument
&what = dir[0]; // and its first character
if (what == '?' || dir[1] == '?') { // First or second character help
help();
return;
}
if (what == '@') { // (Re)construct directory structure
result = dirfile(dir+1);
if (result) cout << "Unable to " << (result == 1 ? "create" : "open")
<< " \"" << mtdirs << "\".";
return;
}
if (what == ';') { // Use a previous directory.
bprev = (dir[1] == ';'); // 0 - previous, 1 - before previous
strcpy(newdir, prev[bprev]);
}
else {
if (dir[1] == ':') { // If there is a drive
newdir[0] = drive = toupper(what);
newdir[1] = ':'; // then make the new directory
newdir[2] = 0; // start with it
dir += 2; // and skip past it
}
else newdir[0] = drive = 0;
if (what == '.') { // Parent shortcut
for (int dots = 0; dir[dots] == '.'; dots++); // Count dots
if (dots > 2) // If more than two then replace
for (int j = 0; j < dots-2; j++) { // each dot after two with "../"
strcat(newdir, "../"); // eg. replace first dot of "..." with
dir++; // "../" to get "../.."
}
}
if (argc > 2 || !find(dir)) { // Two parameters or an unfound subdir
result = partdir(argc-1, argv+1, drive); // requires a search
if (result) { // It hasn't worked
switch (result) {
case 1: cout << "Unable to open \"" << mtdirs << "\"."; break;
case 2: cout << drive << ": has not been scanned."; break;
case 3: cout << "No match found.";
}
cout << endl;
return;
}
}
}
if (chdir(newdir) == -1) {
cout << (newdir[1] == ':' ? "Invalid drive or d" : "D")
<< "irectory does not exist." << endl;
return;
}
if (newdir[1] == ':' && newdir[0] != olddir[0])
setdisk(toupper(newdir[0]) - 'A');
// Is finding string length more efficient than copying the whole array?
movedata(_DS, (unsigned)olddir, mtmem, MAXPATH, strlen(olddir)+1);
if (bprev && strcmp(prev[0], olddir))
movedata(_DS, (unsigned)prev[0], mtmem, MAXPATH+MAXDIR, strlen(prev[0])+1);
}
// See if newdir+path exists (newdir may contain the drive). If path ends in
// "*" it will select the first directory that starts with path.
// Return 0 if a search for path is required, otherwise 1.
int find(const char* path) {
int star = (path[strlen(path)-1] == '*'); // Is there a star?
struct ffblk dir;
if (*path == 0) { // Only a drive has been specified
newdir[2] = '.'; // So chdir will work
newdir[3] = 0;
return 1;
}
strcat(newdir, path);
if (!finddirec(dir, newdir) && // The path doesn't exist,
*path != '.' && !star && // isn't a shortcut, doesn't end in star
!strchr(path, '/') && !strchr(path, '\\')) // and is a one name path
return 0; // then need to search for it
if (star) { // Expand the name found
for (int j = strlen(newdir)-2; // Find where the name starts
j >= 0 && // The very beginning or
newdir[j] != '/' && // after a path or
newdir[j] != '\\' &&
newdir[j] != ':'; // after the drive
j--);
strcpy(newdir+j+1, dir.ff_name); // Replace with the actual name
}
return 1; // Found, or not searching
}
// From the partial names try and find a match. If drive is null then search
// all drives in the file. If a match is found return 0, otherwise return 1
// for unable to open the file; 2 for unscanned drive; 3 for no match.
int partdir(int num, char* partial[], char drive) {
ifstream mt(mtdirs, ios::in | ios::binary);
if (!mt) return 1; // Bit of a problem
char drv, let, // Drive we're on, first letter to match
path[MAXDIR], // A possible path
*dir; // Subdirectory to match
long index, next = 0; // File positions
int found; // Pretty much self-explanatory
for (int j = 0; j < num; j++) strupr(partial[j]);
do { // For each drive required to search
mt.seekg(next); // Point to the drive
mt.get(drv); // Get this drive letter
rlong(mt, next); // Pointer to next drive
if (drive) { // Find the drive we want
while (next && drv != drive) {
mt.seekg(next);
mt.get(drv);
rlong(mt, next);
}
if (drv != drive) return 2; // Drive not in file
}
if (isalpha(let = *partial[num-1])) { // First name start with a letter?
mt.seekg((let-'A') * sizeof(long), ios::cur);
rlong(mt, index); // Then find the appropriate position
if (index == 0) continue; // No directories start with this letter
mt.seekg(index); // Starting position
}
else mt.seekg(26 * sizeof(long), ios::cur); // Start straight after indices
mt.getline(path, MAXDIR);
while (let == *path) {
found = 0; // Number of matches found
dir = path; // First subdirectory
for (int j = num-1; j >= 0 &&
!strncmp(partial[j], dir, strlen(partial[j])); j--) {
found++;
dir = strchr(dir, '/'); // Find the next subdirectory
if (!dir) break; // The path has reached the root
dir++; // Point past the slash
}
if (found == num) { // A successful match
restore(drv, path); // Put the full path into newdir
if (strcmp(newdir, olddir)) { // Only successful if not already here
next = 0; // To terminate the do loop
break; // To terminate the while loop
}
}
mt.getline(path, MAXDIR); // Try another match
}
} while (!drive && next); // Drive not specified and more exist
return (found == num ? 0 : 3);
}
// Take a drive and reversed pathname separated by slashes and create newdir.
// Use memcpy instead of strncat since it is faster (should be, anyway).
void restore(char drv, const char* rev) {
char *beg = newdir + 3; // Where to place the current subdir.
int end = strlen(rev) - 1; // Where it ends
newdir[0] = drv; // The drive where the match was found
newdir[1] = ':';
newdir[2] = '\\'; // Backslashes for comparison purposes
for (int j = end-1; j > 0; j--) { // Search backwards for slashes
if (rev[j] == '/') { // Found one, so from here to the
memcpy(beg, rev+j+1, end-j); // previous match is the subdir.
beg += end-j;
*(beg++) = '\\'; // Add the separator
end = --j; // Ready for the next
}
}
memcpy(beg, rev, ++end); // Final subdirectory
*(beg+end) = 0; // Very important not to forget this
}
// Create the directory structure (see finddirs, index and sort for details).
// If drives is an empty string then scan the drives already in the file,
// otherwise scan those drives specified (assume they're valid).
// Return 0 for all okay; 1 for unable to create file; 2 for unable to open it.
int dirfile(char* drives) {
int cur = getdisk(), // Current drive
drv[26], // Drive numbers (A: = 0)
n = strlen(strupr(drives)), // Number of drives to scan
j, k; // Loop variables
long last; // Last drive position
if (n) { // Specified drives
for (j = 0; j < n; j++) drv[j] = drives[j] - 'A';
cout << "Creating";
}
else { // Drives already there
ifstream mtin(mtdirs, ios::in | ios::binary);
if (!mtin) return 2; // Probably not yet been created
do {
drv[n++] = mtin.get() - 'A';
rlong(mtin, last);
mtin.seekg(last);
} while (last);
mtin.close();
cout << "Updating";
}
cout << " \"" << mtdirs << "\"." << endl;
ofstream mt(mtdirs, ios::out | ios::binary);
if (!mt) return 1; // Uh oh!
dirs = new char*[MaxDirs]; // Create the array of directories
for (j = 0; j < n; j++) {
setdisk(drv[j]); // Make the drive active
cout << "Drive " << char(drv[j]+'A') << " -> Directories = 0";
dirnum = 0;
finddirs("/"); // Get all the directories
cout << " -> Sorting";
qsort((void*)dirs, dirnum, sizeof(dirs[0]), sort);
cout << " -> Indexing";
last = index(mt, drv[j]+'A');
for (k = 0; k < dirnum; k++) // Free the memory for the next drive
delete []dirs[k];
cout << " -> Done." << endl;
}
mt.seekp(last); // Write zero for last drive
wlong(mt, 0);
mt.close();
delete []dirs;
setdisk(cur); // Restore current drive
chdir(olddir); // and directory
return 0;
}
// Starting from startdir recursively find all directories for the current
// drive and store them as a reverse path separated by slashes, ending in a
// newline. eg: "\language\bc" will be stored as "bc/language<\n>".
// Global variables dirs holds the directories; dirnum has the number found.
// The initial starting directory has no parent.
// Use memcpy since I need lengths anyway.
void finddirs(const char* startdir, const char* parent) {
struct ffblk dir;
int nlen; // Length of current subdirectory
static int plen = 0; // Length of startdir's parent
static char *name; // The name of the subdirectory
chdir(startdir);
name = finddirec(dir, "*.*");
while (name) {
// Make some space: length of this dir., slash, length of parent, null
dirs[dirnum] = new char[(nlen = strlen(name)) + plen + 2];
memcpy(dirs[dirnum], name, nlen); // Copy this directory
dirs[dirnum][nlen++] = '/'; // Add the slash
memcpy(dirs[dirnum]+nlen, parent, plen+1); // Add the parent (and null)
plen += nlen; // The new parent length
dirs[dirnum][plen-1] = '\n'; // Last slash becomes newline
cout << "\b\b\b\b" << setw(4) << ++dirnum; // Update and output count
finddirs(name, dirs[dirnum-1]); // Get any subdirectories
chdir(".."); // Back to this one
plen -= nlen; // The old parent length
name = finddirec(dir); // The next directory
}
}
// Find a directory that matches name, ignoring the . and .. directories.
// If name is not given, continue the find.
// Return the name found, or NULL.
char* finddirec(struct ffblk &dir, const char* name) {
const int FA_DIREC = 0x10; // From dos.h
int found;
if (*name) found = findfirst(name, &dir, FA_DIREC);
else found = findnext(&dir);
while (found == 0 && (dir.ff_attrib != FA_DIREC || *dir.ff_name == '.'))
found = findnext(&dir);
return (found == 0 ? dir.ff_name : NULL);
}
// Write and index the directories for drive. First write the drive letter and
// an index to the next drive. Following this are 26 indices for each letter of
// the alphabet. An index of zero means no directories start with that letter.
// Then the directories, one per line, and a blank line to finish.
// It returns the position of the next drive index so zero can be written to
// indicate no more drives. However, os is left at the end of the file.
long index(ofstream& os, char drive) {
char let = 'A'; // Current index letter
long index, lin, din; // File positions
os.put(drive); // Write the drive letter
din = os.tellp(); // The next drive index position
wlong(os, 0); // Where the next drive will start
lin = os.tellp(); // The current letter index position
for (int j = 0; j < 26; j++) wlong(os, 0); // Letters' index positions
for (j = 0; j < dirnum; j++) {
while (*dirs[j] > let) { // In case there are no directories
let++; // that start with let
lin += sizeof(long);
}
if (*dirs[j] == let) { // Write the index for this letter
index = os.tellp(); // This is where it begins
os.seekp(lin); // Go to letter index position
wlong(os, index); // Write the value
os.seekp(0, ios::end); // Back to the end
let++; // Next letter
lin += sizeof(long); // and its index position
}
os << dirs[j]; // Write the directory
}
os << endl; // No more directories
index = os.tellp(); // Store the position for next drive
os.seekp(din);
wlong(os, index);
os.seekp(0, ios::end); // Ready for next drive
return din; // For writing 0 for no more drives
}
// This determines how the directories will be searched. Smaller paths are
// placed before larger paths, non-alphabetical entries are before alphabetical
// directories, and the directories are sorted alphabetically.
int sort(const void *a, const void *b) {
char *dir1 = *(char**)a, // qsort thinks I am sorting an array
*dir2 = *(char**)b; // of pointers - this gets the string
int let1 = isalpha(*dir1), len1, // First directory is alphabetic
let2 = isalpha(*dir2), len2; // Second directory is alphabetic
if (*dir1 == *dir2) { // Both start with the same character
len1 = subs(dir1); // so sort by path length
len2 = subs(dir2);
if (len1 < len2) return -1; // Same size path length will be
else if (len1 > len2) return 1; // sorted by name below
}
if ((let1 && let2) || (!let1 && !let2)) // Both are/are not letters
return strcmp(dir1, dir2); // so sort by name
return (let1 ? 1 : -1); // Letters placed after non-letters
}
// Determine the number of directories in a path for the sort routine.
int subs(const char* path) {
int n = 0; // Number of slashes
for (; *path; path++) if (*path == '/') n++;
return n+1; // Number of paths
}
// Display the help screen.
void help() {
cout << // One really long string
"\n"
"mt - Move To drive/directory. mtmem - resident memory for mt.\n"
"Copyright 1996 Jason Hood. Freeware. Version "version".\n"
"\n"
"mt Display current, previous and before previous directories.\n"
"mt @dc Create directory structure for drives D: and C:.\n"
"mt @ Update directory structure (rescan drives).\n"
"mt ... Equivalent to \"mt ../..\".\n"
"mt; Move back to previous directory.\n"
"mt;; Move back to directory before previous directory.\n"
"mt wg* Move to first subdirectory starting with \"wg\".\n"
" (Only the ending directory can end in \"*\".)\n"
"mt w/g Move to subdirectory \"w\" subsubdirectory \"g\".\n"
"mt wg Move to subdirectory \"wg\". If that fails\n"
" search for a directory that starts with \"wg\".\n"
"mt w g Search for a directory starting with \"w\" that\n"
" has a subdirectory starting with \"g\".\n"
"\n"
"Searches always begin from the root directory.\n"
"\n";
}